from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from pydantic import BaseModel, validator
import re
from typing import Optional, List

from models import get_db, User, Tag, UserTag, create_tables, init_tags
from config.redis import RedisUtils, RedisKeys
from services.sms_service import sms_service
from services.auth_service import jwt_auth

# 创建路由器
router = APIRouter()

# HTTP Bearer认证
security = HTTPBearer()

# 请求模型
class SendCodeRequest(BaseModel):
    phone: str
    
    @validator('phone')
    def validate_phone(cls, v):
        if not re.match(r'^1[3-9]\d{9}$', v):
            raise ValueError('请输入正确手机号')
        return v

class LoginRequest(BaseModel):
    phone: str
    code: str
    
    @validator('phone')
    def validate_phone(cls, v):
        if not re.match(r'^1[3-9]\d{9}$', v):
            raise ValueError('请输入正确手机号')
        return v
    
    @validator('code')
    def validate_code(cls, v):
        if not re.match(r'^\d{6}$', v):
            raise ValueError('请输入6位数字验证码')
        return v

class UsernamePasswordLoginRequest(BaseModel):
    username: str
    password: str
    
    @validator('username')
    def validate_username(cls, v):
        if len(v) < 3 or len(v) > 20:
            raise ValueError('用户名长度应在3-20个字符之间')
        if not re.match(r'^[a-zA-Z0-9_]+$', v):
            raise ValueError('用户名只能包含字母、数字和下划线')
        return v
    
    @validator('password')
    def validate_password(cls, v):
        if len(v) < 6:
            raise ValueError('密码长度至少6位')
        return v

class RegisterRequest(BaseModel):
    username: str
    password: str
    nickname: str
    
    @validator('username')
    def validate_username(cls, v):
        if len(v) < 3 or len(v) > 20:
            raise ValueError('用户名长度应在3-20个字符之间')
        if not re.match(r'^[a-zA-Z0-9_]+$', v):
            raise ValueError('用户名只能包含字母、数字和下划线')
        return v
    
    @validator('password')
    def validate_password(cls, v):
        if len(v) < 6:
            raise ValueError('密码长度至少6位')
        return v
    
    @validator('nickname')
    def validate_nickname(cls, v):
        if len(v) < 2 or len(v) > 20:
            raise ValueError('昵称长度应在2-20个字符之间')
        return v

# 标签相关模型
class TagResponse(BaseModel):
    id: int
    name: str
    icon: str
    hot: int = 0
    parent_id: Optional[int] = None

class UserTagRequest(BaseModel):
    tag_ids: List[int]
    
    @validator('tag_ids')
    def validate_tag_ids(cls, v):
        if not v:
            raise ValueError('至少选择一个兴趣标签')
        if len(v) > 5:
            raise ValueError('最多选择5个兴趣标签')
        if len(set(v)) != len(v):
            raise ValueError('标签ID不能重复')
        return v

class UserTagResponse(BaseModel):
    code: int
    msg: str
    data: Optional[List[TagResponse]] = None

class LoginResponse(BaseModel):
    success: bool
    message: str
    token: Optional[str] = None
    user: Optional[dict] = None

# 依赖注入：获取当前用户
async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db)
) -> User:
    """获取当前登录用户"""
    token = credentials.credentials
    
    # 验证Token
    payload = jwt_auth.verify_token(token)
    if not payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token无效或已过期"
        )
    
    user_id = payload.get("user_id")
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token中缺少用户信息"
        )
    
    # 检查Redis中的Token是否匹配（实现重复登录踢下线）
    redis_token_key = RedisKeys.USER_TOKEN.format(user_id=user_id)
    stored_token = RedisUtils.get(redis_token_key)
    if stored_token != token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token已失效，请重新登录"
        )
    
    # 从数据库获取用户信息
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="用户不存在"
        )
    
    return user

@router.post("/send-code", response_model=dict)
async def send_verification_code(
    request: SendCodeRequest,
    db: Session = Depends(get_db)
):
    """发送验证码接口"""
    phone = request.phone
    
    # 检查发送频率限制
    code_limit_key = RedisKeys.CODE_LIMIT.format(phone=phone)
    if RedisUtils.exists(code_limit_key):
        return {
            "success": False,
            "message": "验证码发送过频繁，请稍后再试"
        }
    
    # 生成验证码
    code = sms_service.generate_verification_code()
    
    # 发送短信
    sms_result = await sms_service.send_verification_code(phone, code)
    if not sms_result["success"]:
        return sms_result
    
    # 缓存验证码（5分钟过期）
    verify_code_key = RedisKeys.VERIFY_CODE.format(phone=phone)
    RedisUtils.set_with_expire(verify_code_key, code, 300)
    
    # 设置发送限制（60秒）
    RedisUtils.set_with_expire(code_limit_key, "1", 60)
    
    return {
        "success": True,
        "message": "验证码发送成功"
    }

@router.post("/login", response_model=LoginResponse)
async def login(
    request: LoginRequest,
    db: Session = Depends(get_db)
):
    """登录接口"""
    phone = request.phone
    code = request.code
    
    # 检查登录锁定状态
    login_lock_key = RedisKeys.LOGIN_LOCK.format(phone=phone)
    if RedisUtils.exists(login_lock_key):
        return LoginResponse(
            success=False,
            message="登录失败次数过多，请5分钟后再试"
        )
    
    # 验证验证码
    verify_code_key = RedisKeys.VERIFY_CODE.format(phone=phone)
    cached_code = RedisUtils.get(verify_code_key)
    
    if not cached_code or cached_code != code:
        # 验证码错误，增加失败计数
        login_fail_key = RedisKeys.LOGIN_FAIL.format(phone=phone)
        fail_count = RedisUtils.incr(login_fail_key, 300)  # 5分钟过期
        
        if fail_count >= 3:
            # 达到失败次数上限，锁定账号
            RedisUtils.set_with_expire(login_lock_key, "1", 300)  # 锁定5分钟
            return LoginResponse(
                success=False,
                message="验证码错误，已连续失败3次，账号锁定5分钟"
            )
        else:
            return LoginResponse(
                success=False,
                message=f"验证码错误，还可尝试{3-fail_count}次"
            )
    
    # 验证码正确，查找或创建用户
    user = db.query(User).filter(User.phone == phone).first()
    
    if not user:
        # 新用户自动注册
        nickname = f"用户{phone[-4:]}"  # 默认昵称：用户+手机号后4位
        user = User(phone=phone, nickname=nickname)
        db.add(user)
        db.commit()
        db.refresh(user)
    
    # 生成JWT Token
    token = jwt_auth.generate_token(user.id, user.phone)
    
    # 存储Token到Redis（覆盖旧Token，实现重复登录踢下线）
    user_token_key = RedisKeys.USER_TOKEN.format(user_id=user.id)
    RedisUtils.set(user_token_key, token)
    
    # 清理验证码和失败计数缓存
    RedisUtils.delete(verify_code_key)
    RedisUtils.delete(RedisKeys.LOGIN_FAIL.format(phone=phone))
    
    return LoginResponse(
        success=True,
        message="登录成功",
        token=token,
        user={
            "id": user.id,
            "phone": user.phone,
            "nickname": user.nickname,
            "created_at": user.created_at.isoformat()
        }
    )

@router.get("/profile")
async def get_profile(current_user: User = Depends(get_current_user)):
    """获取用户信息接口（需要认证）"""
    return {
        "success": True,
        "data": {
            "id": current_user.id,
            "phone": current_user.phone,
            "nickname": current_user.nickname,
            "created_at": current_user.created_at.isoformat()
        }
    }

@router.post("/logout")
async def logout(current_user: User = Depends(get_current_user)):
    """退出登录接口"""
    # 删除Redis中的Token
    user_token_key = RedisKeys.USER_TOKEN.format(user_id=current_user.id)
    RedisUtils.delete(user_token_key)
    
    return {
        "success": True,
        "message": "退出登录成功"
    }

@router.post("/register", response_model=LoginResponse)
async def register(request: RegisterRequest, db: Session = Depends(get_db)):
    """用户注册接口"""
    # 检查用户名是否已存在
    existing_user = db.query(User).filter(User.username == request.username).first()
    if existing_user:
        return LoginResponse(
            success=False,
            message="用户名已存在"
        )
    
    # 创建新用户
    user = User(
        username=request.username,
        nickname=request.nickname
    )
    user.set_password(request.password)
    
    try:
        db.add(user)
        db.commit()
        db.refresh(user)
        
        # 生成JWT Token
        token = jwt_auth.generate_token(user.id, user.username)
        
        # 存储Token到Redis
        user_token_key = RedisKeys.USER_TOKEN.format(user_id=user.id)
        RedisUtils.set(user_token_key, token)
        
        return LoginResponse(
            success=True,
            message="注册成功",
            token=token,
            user={
                "id": user.id,
                "username": user.username,
                "nickname": user.nickname,
                "created_at": user.created_at.isoformat()
            }
        )
    except Exception as e:
        db.rollback()
        return LoginResponse(
            success=False,
            message=f"注册失败: {str(e)}"
        )

@router.post("/username-login", response_model=LoginResponse)
async def username_login(request: UsernamePasswordLoginRequest, db: Session = Depends(get_db)):
    """账号密码登录接口"""
    # 查找用户
    user = db.query(User).filter(User.username == request.username).first()
    
    if not user:
        return LoginResponse(
            success=False,
            message="用户名或密码错误"
        )
    
    # 验证密码
    if not user.check_password(request.password):
        return LoginResponse(
            success=False,
            message="用户名或密码错误"
        )
    
    # 生成JWT Token
    token = jwt_auth.generate_token(user.id, user.username)
    
    # 存储Token到Redis（覆盖旧Token，实现重复登录踢下线）
    user_token_key = RedisKeys.USER_TOKEN.format(user_id=user.id)
    RedisUtils.set(user_token_key, token)
    
    return LoginResponse(
        success=True,
        message="登录成功",
        token=token,
        user={
            "id": user.id,
            "username": user.username,
            "nickname": user.nickname,
            "phone": user.phone,
            "created_at": user.created_at.isoformat()
        }
    )

# 标签相关接口
@router.get("/tags", response_model=List[TagResponse])
async def get_tags(db: Session = Depends(get_db)):
    """获取所有兴趣标签"""
    try:
        # 尝试从Redis缓存获取
        cached_tags = RedisUtils.get("tags:all")
        if cached_tags:
            import json
            tags_data = json.loads(cached_tags)
            return [TagResponse(**tag) for tag in tags_data]
        
        # 从数据库查询
        tags = db.query(Tag).order_by(Tag.hot.desc()).all()
        
        # 转换为响应格式
        tags_response = []
        for tag in tags:
            tags_response.append(TagResponse(
                id=tag.id,
                name=tag.name,
                icon=tag.icon,
                hot=tag.hot or 0,
                parent_id=tag.parent_id
            ))
        
        # 缓存到Redis（1小时过期）
        import json
        RedisUtils.set("tags:all", json.dumps([tag.dict() for tag in tags_response]), 3600)
        
        return tags_response
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取标签失败: {str(e)}")

@router.get("/user/tags", response_model=UserTagResponse)
async def get_user_tags(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db)
):
    """获取当前用户已选标签"""
    try:
        # 验证JWT Token
        payload = jwt_auth.verify_token(credentials.credentials)
        if not payload:
            raise HTTPException(status_code=401, detail="Token无效")
        user_id = payload.get("user_id")
        
        # 尝试从Redis缓存获取
        user_tags_key = f"user:tags:{user_id}"
        cached_tags = RedisUtils.get(user_tags_key)
        if cached_tags:
            import json
            tag_ids = json.loads(cached_tags)
            if not tag_ids:
                return UserTagResponse(code=0, msg="用户未选择标签", data=[])
        
        # 从数据库查询用户已选标签
        user_tags = db.query(UserTag).filter(UserTag.user_id == user_id).all()
        
        if not user_tags:
            # 缓存空结果
            RedisUtils.set(user_tags_key, "[]", 86400)
            return UserTagResponse(code=0, msg="用户未选择标签", data=[])
        
        # 获取标签详情
        tag_ids = [ut.tag_id for ut in user_tags]
        tags = db.query(Tag).filter(Tag.id.in_(tag_ids)).all()
        
        # 转换为响应格式
        tags_response = []
        for tag in tags:
            tags_response.append(TagResponse(
                id=tag.id,
                name=tag.name,
                icon=tag.icon,
                hot=tag.hot or 0,
                parent_id=tag.parent_id
            ))
        
        # 缓存到Redis（24小时过期）
        import json
        RedisUtils.set(user_tags_key, json.dumps(tag_ids), 86400)
        
        return UserTagResponse(code=0, msg="获取成功", data=tags_response)
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取用户标签失败: {str(e)}")

@router.post("/user/tags", response_model=UserTagResponse)
async def submit_user_tags(
    request: UserTagRequest,
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db)
):
    """提交用户选择的标签"""
    try:
        # 验证JWT Token
        payload = jwt_auth.verify_token(credentials.credentials)
        if not payload:
            raise HTTPException(status_code=401, detail="Token无效")
        user_id = payload.get("user_id")
        
        # 验证标签ID是否存在
        valid_tags = db.query(Tag).filter(Tag.id.in_(request.tag_ids)).all()
        if len(valid_tags) != len(request.tag_ids):
            invalid_ids = set(request.tag_ids) - {tag.id for tag in valid_tags}
            raise HTTPException(status_code=400, detail=f"无效的标签ID: {list(invalid_ids)}")
        
        # 开始事务
        try:
            # 删除用户旧的标签选择
            db.query(UserTag).filter(UserTag.user_id == user_id).delete()
            
            # 批量插入新的标签选择
            new_user_tags = []
            for tag_id in request.tag_ids:
                user_tag = UserTag(user_id=user_id, tag_id=tag_id)
                new_user_tags.append(user_tag)
                db.add(user_tag)
            
            # 更新标签热度值
            for tag in valid_tags:
                tag.hot += 1
            
            db.commit()
            
            # 清除相关缓存
            RedisUtils.delete("tags:all")  # 清除标签列表缓存
            RedisUtils.delete(f"user:tags:{user_id}")  # 清除用户标签缓存
            
            return UserTagResponse(code=0, msg="兴趣选择成功")
            
        except Exception as e:
            db.rollback()
            raise e
            
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"提交标签失败: {str(e)}")
